<?php
namespace frontend\controllers;

use common\config\TimeFormator;
use common\exception\Message as ErrorMessage;
use common\fixtures\HttpTokenAuth;
use common\models\IcoMember;
use common\service\sms\Client;
use lanyife\sdk\google\Authenticator;
use yii\base\Event;
use yii\base\InvalidConfigException;
use yii\captcha\CaptchaValidator;
use yii\data\ActiveDataProvider;
use yii\helpers\HtmlPurifier;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\web\Response;


/**
 * Base controller
 */
class BaseController extends Controller
{

    /**
     * @var bool 取消post 验证
     */
    public $enableCsrfValidation = false;

    /**
     * 异常 是否强制返回json
     * @var bool
     */
    public $returnJson = false;

    /**
     * @var IcoMember
     */
    public $member;

    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors['authenticator'] = [
            'class' => HttpTokenAuth::className(),
            'tokenParam' => 'Token'
        ];
        return $behaviors;
    }

    /**
     * @param \yii\base\Action $action
     * @return bool
     * @throws \yii\web\BadRequestHttpException
     */
    public function beforeAction($action)
    {
        $this->formatParam();
        return parent::beforeAction($action);
    }

    public static $cannotExportFields = ['mobiles','mobile'];

    public static $canExportFields = [];

    public function init()
    {
        $this->formatParam();
        $this->on(self::EVENT_BEFORE_ACTION, [$this, 'checkAccessRule']);
    }

    protected function formatParam()
    {
        $get_data = \Yii::$app->request->get('data',[]);
        if(is_array($get_data)){
            \Yii::$app->request->setQueryParams(array_merge($get_data,\Yii::$app->request->get()));
        }
        $post_data = \Yii::$app->request->post('data',[]);
        if(is_array($post_data)){
            \Yii::$app->request->setBodyParams(array_merge($post_data,\Yii::$app->request->post()));
        }
        if(\Yii::$app->request->isPost && empty($post_data)){
            $body = file_get_contents('php://input');
            $payload = json_decode($body,true);
            if(is_array($payload)){
                $post_data = $payload;
            }
            else{
                parse_str($body,$post_data);
            }
            \Yii::$app->request->setBodyParams(array_merge($post_data,\Yii::$app->request->post()));
        }
    }

    /**
     * @param Event $event
     * @throws InvalidConfigException
     */
    public function checkAccessRule(Event $event)
    {
        if (!isset($event->sender->action->actionMethod)) {// inlineaction 出错
            return;
        }
        $reflect = new \ReflectionMethod($event->sender, $event->sender->action->actionMethod);
        $doc = $reflect->getDocComment();
        if (false === stripos($doc, '<roles>') || false === stripos($doc, '</roles>')) {
            return;
        }
        preg_match('/<roles>(.*)<\/roles>/iUs', $doc, $matches);
        if (!isset($matches[1])) {
            throw new InvalidConfigException("Cannot parser roles " . $doc);
        }

        $splitSymbol = strpos($doc, "\r\n") !== false ? "\r\n" : "\n";
        $roles = explode($splitSymbol, $matches[1]);
        // 调用所有的验证
        array_map(function($value) {
            if (false === strpos($value, '@')) {
                return;
            };
            $params = explode(' ', ltrim(strstr($value, '@'), '@'));
            $method = array_shift($params);
            \Yii::info("Api access check $method");
            call_user_func([$this, $method], $params);
        }, $roles);
    }

    /**
     * need user
     */
    protected function loginrequired()
    {
        if(\Yii::$app->user->isGuest){
            $this->returnJson(403,'需要登录!');
        }
        $this->member = \Yii::$app->user->identity;
    }

    /**
     * need google code
     */
    protected function google_verify()
    {
        $data = array_merge(\Yii::$app->request->get(),\Yii::$app->request->post());
        if(!isset($data['google_code'])||empty($data['google_code'])){
            $this->returnJson(503,'请填写谷歌验证码！');
        }
        $code = $data['google_code'];
        $google = new Authenticator();
        if(empty($this->member->google_key)){
            return true;
        }
        $r = $google->verifyCode($this->member->google_key,$code);
        if(!$r){
            $this->returnJson(503,'谷歌验证码错误！');
        }
    }

    /**
     * need image code
     */
    protected function image_verify()
    {
        $data = array_merge(\Yii::$app->request->get(),\Yii::$app->request->post());
        if(!isset($data['image_code'])||empty($data['image_code'])){
            $this->returnJson(503,'请填写图形验证码！');
        }
        $code = $data['image_code'];
        $imgVerifyCode = HtmlPurifier::process($code);
        $caprcha = new CaptchaValidator();
        $r = $caprcha->validate($imgVerifyCode);
        if(!$r){
            $this->returnJson(503,'图形验证码错误！');
        }
    }

    /**
     * need sms code
     */
    protected function sms_verify()
    {
        $data = array_merge(\Yii::$app->request->get(),\Yii::$app->request->post());
        if(!isset($data['code'])||empty($data['code'])){
            $this->returnJson(503,'请填写短信验证码！');
        }
        $code = $data['code'];
        $this->checkSmsCode($this->member->mobile,$code);
    }

    /**
     * @param $mobile
     * @param $code
     * @return bool
     * @throws NotFoundHttpException
     */
    public function checkSmsCode($mobile,$code)
    {
        $this->returnJson = true;
        Client::getInstance()->setMobile($mobile)
            ->check($code);
        return true;
    }

    /**
     * @param string $id
     * @param array $params
     * @return mixed
     * @throws \Exception
     */
    public function runAction($id, $params = [])
    {
        $transaction = \Yii::$app->getDb()->getTransaction();
        empty($transaction) && $transaction = \Yii::$app->getDb()->beginTransaction();
        try{
            $result = parent::runAction($id,$params);
            $transaction&&$transaction->commit();
            return $result;
        }
        catch(\Exception $e) {
            $this->_handleError($e);
        }
    }

    /**
     * @param int $code
     * @param string $msg
     * @param array $data
     * @throws \yii\base\ExitException
     */
    public function returnJson($code=200,$msg='ok',$data=[])
    {
        \Yii::$app->response->content = json_encode($this->renderJson($code,$msg,$data));
        \Yii::$app->response->send();
        \Yii::$app->end();
    }

    /**
     * @param int $code
     * @param string $msg
     * @param array $data
     * @return string
     */
    public function renderJson($code=200,$msg='ok',$data=[])
    {
        \Yii::$app->response->format = Response::FORMAT_JSON;
        return [
            'code' => $code,
            'msg' => ErrorMessage::get($code,$msg),
            'serverTime' => TimeFormator::getCurrentMicroTime(),
            'data' => $data
        ];
    }

    /**
     * 返回分页列表
     * @param ActiveDataProvider $dataProvider
     * @param array $list
     * @param array $option
     * @return string
     */
    public function renderPageList(ActiveDataProvider $dataProvider,array &$list,array $option=[])
    {
        $pageCount = ceil($dataProvider->getTotalCount()/$dataProvider->getPagination()->pageSize);
        if(\Yii::$app->request->get('page',1)>$pageCount){
            //是否超出分页 就返回空数据
            $list = [];
        }
        if(\Yii::$app->request->get('export',0)==1){
            $this->autoExcel($list);
        }
        $data = [
            'list' => $list,
            'page' => $dataProvider->getPagination()->page+1,
            'pageSize' => $dataProvider->getPagination()->pageSize,
            'pageCount' => $pageCount,
            'allCount' => $dataProvider->getTotalCount()
        ];
        $data = array_merge( $data,$option);
        return $this->renderJson(200,'ok',$data);
    }

    /**
     * @param \Exception $e
     * @throws \Exception $e
     */
    private function _handleError($e)
    {
        $transaction = \Yii::$app->getDb()->getTransaction();
        $transaction&&$transaction->rollBack();
        $code = $e->getCode();
        $code = ($code==0) ? 500 : $code;
        $data = [
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'request' => array_merge(\Yii::$app->request->get(),\Yii::$app->request->post()),
            'msg' => $e->getMessage(),
            'trace' => $e->getTraceAsString(),
            'controller' => \Yii::$app->controller->id,
            'action' => \Yii::$app->controller->action->id,
        ];
        \Yii::error($data);
        $data = YII_DEBUG ? $data : $data;
        if(\Yii::$app->request->isAjax || $this->returnJson ){
            $this->returnJson($code,$e->getMessage(),$data);
        }
        else{
            throw $e;
        }
    }

    /**
     * 生成excel
     * @param array $list
     */
    public function autoExcel(array $list){
        if(empty($list)){
            $this->alertMessage('当前导出数据为空！');
        }
        //处理数组
        $data=$this->arrayData($list);
        $headTitle = array_keys($data[0]);
        foreach($headTitle as $k=>$v){
            if(in_array($v,static::$cannotExportFields)){
                unset($headTitle[$k]);
                continue;
            }
            $headTitle[$k] = \Yii::t('user_excel',$v);
        }
        $file = md5(\Yii::$app->controller->id.\Yii::$app->controller->action->id.time());
        $this->excelPull($file,$headTitle,$data);
    }

    /**
     * @param $list
     * @return array
     */
    public function arrayData($list)
    {
        $array = [];
        foreach($list as $key=>$val){
            $array[$key] = $this->treeData($val);
        }
        return $array;
    }

    /**
     * 递归将多维数组转为一维数组
     * @param $row
     * @param string $key
     * @return array
     */
    protected function treeData($row,$key='')
    {
        $data = [];
        foreach($row as $k => $v){
            $key1 = $key ? ($key.'_'.$k) : $k;
            ($v instanceof \yii\db\ActiveRecord) && $v = $v->getAttributes();
            is_object($v) && $v = (array)$v;
            if(is_array($v)){
                $data = array_merge($data,$this->treeData($v,$key1));
            }
            else{
                $data[$key1] = $v;
            }
        }
        return $data;
    }

    /**
     * 生成excel
     * @param $name string 文件名
     * @param $header array 列名
     * @param $dataResult array 数据
     * @param $headtitle string 未知
     */
    public function excelPull($name='',$header=[],$dataResult=[],$headtitle=''){
        $headTitle = '';
        $headtitle= "<tr style='height:50px;border-style:none;><td border=\"0\" style='height:60px;width:270px;font-size:22px;' colspan='11' >{$headTitle}</th></tr>";
        $titlename = "<tr>";
        foreach($header as $v){
            $titlename .= "<td>$v</td>";
        }
        $titlename .= "</tr>";

        $fileName = date("Y-m-d")."-".$name.".xls";
        $this->excelData($dataResult,$titlename,$headtitle,$fileName);
    }
    /**
     * 下载excel
     * @param $datas
     * @param $titlename
     * @param string $title
     * @param string $filename
     */
    public function excelData($datas,$titlename,$title='',$filename=''){
        $str = "<html xmlns:o=\"urn:schemas-microsoft-com:office:office\"\r\nxmlns:x=\"urn:schemas-microsoft-com:office:excel\"\r\nxmlns=\"http://www.w3.org/TR/REC-html40\">\r\n<head>\r\n<meta http-equiv=Content-Type content=\"text/html; charset=utf-8\">\r\n</head>\r\n<body>";
        $str .="<table border=1>".$title.$titlename;
        $str .= '';
        foreach ($datas  as $key=> $rt )
        {
            $str .= "<tr>";
            foreach($rt as $k => $v){
                if(is_object($v)){
                    $v = json_encode($v);
                }
                $str .= "<td>{$v}</td>";
            }
            $str .= "</tr>\n";
        }
        $str .= "</table></body></html>";
        header( "Content-Type: application/vnd.ms-excel; name='excel'" );
        header( "Content-type: application/octet-stream" );
        header( "Content-Disposition: attachment; filename=".$filename );
        header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" );
        header( "Pragma: no-cache" );
        header( "Expires: 0" );
        exit( $str );
    }

    /**
     * @param string $message
     */
    protected function alertMessage($message='')
    {
        exit(sprintf('<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><script>alert("%s");history.go(-1);</script>',$message));
    }
}
